using M13.Utility;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace M13.DataStructure
{
    public class DynamicExpansionArray<t>
    {
        /// <summary>
        /// array�ĳ��ȱ�����maxBucketLength������
        /// </summary>
        public t[] Array { get; private set; }
        private t[] _buffer;
        public List<EmptyTag> EmptyTags { get; private set; } = new List<EmptyTag>();
        private ArrayPool<t> _arrayPool = ArrayPool<t>.Create(256, 8192);
        private int _maxBucketLength = 256;


        /// <summary>
        /// ��ʼ����̬��������
        /// </summary>
        /// <param name="length">��ʼ���ȣ���Ϊ128��������</param>
        public DynamicExpansionArray(int length)
        {
            Array = _arrayPool.Rent(length);
            EmptyTags.Add(new EmptyTag(0, length));
        }

        public DynamicExpansionArray(t[] src)
        {
            Array = _arrayPool.Rent(src.Length);
            Buffer.BlockCopy(src, 0, Array, 0, src.Length);
            EmptyTags.Add(new EmptyTag(src.Length, Array.Length - src.Length));
        }

        public void Recycle(t[] src)
        {
            System.Array.Clear(Array, 0, Array.Length);
            EmptyTags.Clear();
            Array = src;
        }


        public int AddArray(t[] src, int effectiveLength)
        {
            int insertIndex = -1;
            EmptyTagMerge();
            for (int i = 0; i < EmptyTags.Count; i++)
            {
                //�ҵ����������ı�ǩ
                if (EmptyTags[i].length >= effectiveLength)
                {
                    Buffer.BlockCopy(src, 0, Array, EmptyTags[i].index, effectiveLength);
                    insertIndex = EmptyTags[i].index;
                    EmptyTag e = EmptyTags[i];
                    EmptyTags[i] = new EmptyTag(e.index + effectiveLength, e.length - effectiveLength);
                    return insertIndex;
                }
            }
            ExpandArrays(effectiveLength);
            
            Buffer.BlockCopy(src, 0, Array, EmptyTags[EmptyTags.Count - 1].index, effectiveLength);
            insertIndex = EmptyTags[EmptyTags.Count - 1].index;
            EmptyTag e1 = EmptyTags[EmptyTags.Count - 1];
            EmptyTags[EmptyTags.Count - 1] = new EmptyTag(e1.index + effectiveLength, e1.length - effectiveLength);
            return insertIndex;
        }

        public ArrayPool<t> GetArrays(out t[] dst, int srcOffset, int count, bool clear)
        {
            //������������ǲ��ǿհ�����
            for (int i = 0; i < EmptyTags.Count; i++)
            {
                if (srcOffset > EmptyTags[i].index && srcOffset < EmptyTags[i].index + EmptyTags[i].length) 
                    Debug.Log("��ȡ����������");
            }

            dst = new t[count];
            Buffer.BlockCopy(Array, srcOffset, dst, 0, count);
            if (clear)
            {
                System.Array.Clear(Array, srcOffset, count);
                EmptyTag emptyTag = new EmptyTag(srcOffset, count);
                EmptyTags.Add(emptyTag);
            }
            EmptyTagMerge();
            return _arrayPool;
        }

        private void ExpandArrays(int addedArrLength)
        {
            EmptyTagMerge();
            int expandLength = addedArrLength + _maxBucketLength;
            int index = Array.Length;
            _buffer = _arrayPool.Rent(expandLength + Array.Length);
            Buffer.BlockCopy(Array, 0, _buffer, 0, Array.Length);
            _arrayPool.Return(Array, true);
            Array = _buffer;
            EmptyTags.Add(new EmptyTag(EmptyTags[EmptyTags.Count - 1].index + EmptyTags[EmptyTags.Count - 1].length, EmptyTags[EmptyTags.Count - 1].length + expandLength));
            EmptyTagMerge();
        }

        /// <summary>
        /// ������ܻ�������λ����䣬���ı�ÿһ�����ݵ���ʼ����
        /// </summary>
        public void ArrayCompaction(ChunkDataBoot[] dataBoots)
        {
            if(dataBoots == null || dataBoots.Length != 256)
            {
                Debug.Log("��������������Ϊ�ջ򳤶ȴ���");
            }
            EmptyTagMerge();
            int emptyLength = 0;
            for (int i = 0; i < EmptyTags.Count; i++)
            {
                emptyLength += EmptyTags[i].length;
            }
            int effectiveLength = Array.Length - emptyLength;

            _buffer = _arrayPool.Rent(effectiveLength);

            for(int i = 0, dstOffset = 0; i < dataBoots.Length; i++)
            {
                Buffer.BlockCopy(Array, dataBoots[i].dataOffset, _buffer, dstOffset, dataBoots[i].GetTotalLength());
                dstOffset += dataBoots[i].GetTotalLength();
                dataBoots[i].dataOffset = dstOffset;
            }
            _arrayPool.Return(Array, true);
            Array = _buffer;
        }

        /// <summary>
        /// �ձ�ǩ�ϲ�
        /// </summary>
        private void EmptyTagMerge()
        {
            for(int i = 0; i < EmptyTags.Count; i++)
            {
                if (EmptyTags[i].length == 0)
                    EmptyTags.RemoveAt(i);
            }
            EmptyTags = EmptyTags.OrderBy(e => e.index).ToList();
            for (int i = 0; i < EmptyTags.Count; i++)
            {
                
                for (int j = 0; j < EmptyTags.Count; j++)
                {
                    if (EmptyTags[i].index + EmptyTags[i].length == EmptyTags[j].index && j - i == 1)
                    {
                        EmptyTag emptyTag = new EmptyTag(EmptyTags[i].index, EmptyTags[i].length + EmptyTags[j].length);
                        EmptyTags.RemoveAt(i);
                        EmptyTags.RemoveAt(j - 1);
                        EmptyTags.Add(emptyTag);
                        i = 0;
                        j = 0;
                        EmptyTags = EmptyTags.OrderBy(e => e.index).ToList();
                    }
                }
            }
        }
    }

    [Serializable]
    public struct EmptyTag
    { 
        public int index, length; 
        public EmptyTag(int index,int length)
        {
            this.index = index; this.length = length;
        }
    }
}